package com.iims.flowable.service.impl;

import com.iims.common.core.domain.AjaxResult;
import com.iims.flowable.common.cmd.GetProcessDefinitionInfoCmd;
import com.iims.flowable.factory.BaseFlowResource;
import com.iims.flowable.flow.ObjectUtils;
import com.iims.flowable.flow.domain.FlowableCategory;
import com.iims.flowable.flow.vo.request.FlowActiveSuspendRequest;
import com.iims.flowable.mapper.FlowableCommonMapper;
import com.iims.flowable.service.IFlowDefinitionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.job.api.Job;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * 流程定义
 *
 * @author fanpei
 * @date 2021-04-03
 */
@Service
@Slf4j
public class FlowDefinitionServiceImpl extends BaseFlowResource implements IFlowDefinitionService {

    @Autowired
    private FlowableCommonMapper commonMapper;

    @Override
    public ProcessDefinition getProcessDefinitionById(String processDefinitionId) {
        return managementService.executeCommand(new GetProcessDefinitionInfoCmd(processDefinitionId, null, null));
    }

    @Override
    public List<FlowableCategory> categoryList() {
        return commonMapper.selectCategoryList();
    }

    /**
     * 导入流程文件
     */
    @Override
    public AjaxResult importFile(String name, String category, String filename, InputStreamSource file, HttpServletRequest request) {

        if (!(request instanceof MultipartHttpServletRequest)) {
            throw new IllegalArgumentException("request must instance of MultipartHttpServletRequest");
        }
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        if (multipartRequest.getFileMap().size() == 0) {
            throw new IllegalArgumentException("request file is empty");
        }
        boolean isFileNameInValid = ObjectUtils.isEmpty(filename) || !(filename.endsWith(".bpmn20.xml") || filename.endsWith(".bpmn"));
        if (isFileNameInValid) {
            throw new IllegalArgumentException("Request file must end with .bpmn20.xml,.bpmn");
        }
        InputStream in = null;
        try {
            in = file.getInputStream();
            Deployment deploy = repositoryService.createDeployment().addInputStream(filename, in).name(name).category(category).deploy();
            ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
            repositoryService.setProcessDefinitionCategory(definition.getId(), category);
        } catch (Exception e) {
            log.error("导入失败:", e);
            return AjaxResult.error(e.getMessage());
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                log.error("关闭输入流出错", e);
            }
        }
        return AjaxResult.success("导入成功");

    }

    /**
     * 读取xml
     *
     * @param processDefinitionId
     * @return
     */
    @Override
    public AjaxResult readXml(String processDefinitionId) throws IOException {
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
        String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.toString());
        return AjaxResult.success("", result);
    }

    /**
     * 读取xml
     *
     * @param processDefinitionId
     * @return
     */
    @Override
    public InputStream readImage(String processDefinitionId) {
        permissionService.validateReadPermissionOnProcessDefinition(getLoginUserCode(), processDefinitionId);
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        InputStream imageStream = repositoryService.getProcessDiagram(processDefinition.getId());
        if (imageStream == null) {
            throw new FlowableException(messageFormat("Process definition image is not found with id {0}",
                    processDefinitionId));
        }
        //获得图片流
        DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        //输出为图片
        return diagramGenerator.generateDiagram(
                bpmnModel,
                "png",
                Collections.emptyList(),
                Collections.emptyList(),
                "宋体",
                "宋体",
                "宋体",
                null,
                1.0,
                false);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void activate(FlowActiveSuspendRequest actionRequest) {
        String processDefinitionId = actionRequest.getProcessDefinitionId();
        ProcessDefinition processDefinition = getProcessDefinitionById(processDefinitionId);
        if (!processDefinition.isSuspended()) {
            throw new FlowableException("Process definition is not suspended with id " + processDefinitionId);
        }
        repositoryService.activateProcessDefinitionById(processDefinitionId,
                actionRequest.isIncludeProcessInstances(), new Date());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void suspend(FlowActiveSuspendRequest actionRequest) {
        String processDefinitionId = actionRequest.getProcessDefinitionId();
        ProcessDefinition processDefinition = getProcessDefinitionById(processDefinitionId);
        if (processDefinition.isSuspended()) {
            throw new FlowableException("Process definition is already suspended with id " + processDefinitionId);
        }
        repositoryService.suspendProcessDefinitionById(processDefinition.getId(),
                actionRequest.isIncludeProcessInstances(), new Date());
    }

    /**
     * 删除流程定义
     */
    @Override
    public void delete(String processDefinitionId, boolean cascade) {
        ProcessDefinition processDefinition = getProcessDefinitionById(processDefinitionId);
        if (processDefinition.getDeploymentId() == null) {
            throw new FlowableException("Process definition has not deployed with id " + processDefinitionId);
        }
        if (cascade) {
            List<Job> jobs = managementService.createTimerJobQuery().processDefinitionId(processDefinitionId).list();
            for (Job job : jobs) {
                managementService.deleteTimerJob(job.getId());
            }
            repositoryService.deleteDeployment(processDefinition.getDeploymentId(), true);
        } else {
            long processCount =
                    runtimeService.createProcessInstanceQuery().processDefinitionId(processDefinitionId).count();
            if (processCount > 0) {
                throw new FlowableException("There are running instances with process definition id " + processDefinitionId);
            }
            long jobCount = managementService.createTimerJobQuery().processDefinitionId(processDefinitionId).count();
            if (jobCount > 0) {
                throw new FlowableException("There are running time jobs with process definition id " + processDefinitionId);
            }
            repositoryService.deleteDeployment(processDefinition.getDeploymentId());
        }
    }


}
